package com.dys.ai.controller;

import com.dys.ai.common.interceptor.LoggingAdvisor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import java.time.LocalDate;

@RestController
public class AiController {

    private final ChatClient chatClient;

    private String systemChatMessage = """
            您是“大源数”智能铁路公司的客户聊天支持代理。请以友好、乐于助人且愉快的方式来回复。
                        您正在通过在线聊天系统与客户进行互动。
                        在提供有关购票或退票的信息之前，您必须始终
                        从用户处获取以下信息：乘客姓名、出发站、到达站、出发时间、车次、座位类型(商务座/一等座/二等座)。
                        在询问用户之前，请检查消息历史记录以获取此信息。
                        如果用户想要查询订单信息,您必须始终从用户处获取以下信息：乘客姓名、车次
                        在改签或退票之前，请先获取预订信息并且告知条款待用户回复确定之后才进行更改或退订的function-call
                        如果用户想要改签，请先让用户提供以下信息：原始订单号、新的车次编号、新的座位类型(比如1A)，用户提供之后，让用户确认一遍，再执行修改的function-call
                        请讲中文。
                        今天的日期是 {current_date}.
            """;

    public AiController(ChatClient.Builder chatClientBuilder, VectorStore vectorStore, ChatMemory chatMemory) {
        this.chatClient = chatClientBuilder
                .defaultSystem("""
                        您是「大源数」铁路的智能客服千问，请以✨亲切热情✨专业高效✨的风格服务客户，全程使用中文沟通。
                        您正在通过在线聊天系统与客户进行互动。
                        在提供有关购票或退票的信息之前，您必须始终
                        从用户处获取以下信息：✅乘客姓名｜✅出发站｜✅到达站｜✅出发日期｜✅车次号｜✅座位类型(商务 / 一等 / 二等)。
                        在询问用户之前，先查历史对话，已存在的信息不再重复询问。
                        如果用户想要查询订单信息,您必须始终从用户处获取以下信息：✅乘客姓名｜✅车次号
                        在改签或退票之前，请先获取预订信息并且告知条款待用户回复确定之后才进行更改或退订的function-call
                        如果用户想要改签，请先让用户提供以下信息：✅原始订单号｜✅新车次编号｜✅新车厢编号｜✅新座位号(如 3A)，用户提供之后，让用户确认一遍，再执行修改的function-call
                        请讲中文。
                        今天的日期是 {current_date}.
                        """)
                // 开启对话记忆功能
                .defaultAdvisors(new PromptChatMemoryAdvisor(chatMemory),
                        // RAG 空字符串检索所有内容
                        new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults().build()),
                        new LoggingAdvisor())
                .defaultFunctions("getOrderDetails", "buyTicket", "reBook", "refundTicket")
                .build();
    }

    @GetMapping(value = "/ai/generateStreamAsString", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> generateStreamAsString(@RequestParam(value = "message", defaultValue = "讲个笑话") String message) {
        Flux<String> content = this.chatClient.prompt()
                .user(message)
                .system(s -> s.param("current_date", LocalDate.now().toString()))
                // .advisors() 检索内容根据对话内容来
                // 配置记忆长度
                .advisors(advisors -> advisors.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100))
                .stream()
                .content();
        return content.concatWith(Flux.just("[complete]"));
    }
}
